package dk.hewison.util;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Iterator;



/**
 * @author John Hewison
 *         Date: May 8, 2003
 */
public class HxImageIO {
    //private HashMap mReaders;

    public final static String[] FILE_TYPES = ImageIO.getReaderFormatNames();// = {"jpg", "jpeg", "png", "gif", "bmp", "tif", "pnm", "raw"};

    private static final float QUALITY = (float) 0.9;

    public static final int BIG_THUMB = 600;
    public static final int LITTLE_THUMB = 144;

    public HxImageIO() {
        //init();
        //FILE_TYPES = ImageIO.getReaderFormatNames();
        for (int i = 0; i < FILE_TYPES.length; i++) {

            Log.debug("HxImageIO.HxImageIO" + FILE_TYPES[i]);
        }
    }

    /**
     * Creates a HxImage containing a thumbnail and an ImageInfo.
     */
    /*public HxImage readThumbnail( final File imageFile ) throws Exception
    {
       if( HxLog.DEBUG_ON )
          HxLog.debug( getClass().getName() + ".readThumbnail " );

       final String ext = Util.getExtension( imageFile.getName() );
       final ImageReader reader = getImageReader( ext );
       final ImageInputStream iis = ImageIO.createImageInputStream( imageFile );
       reader.setInput( iis );
       final ImageReadParam param = reader.getDefaultReadParam();

       final int height = reader.getHeight( 0 );
       final int width = reader.getWidth( 0 );

       final int scale = calculateScale( width, height );
       param.setSourceSubsampling( scale, scale, 0, 0 );
       //if( param.canSetSourceRenderSize() )
       //   param.getSourceRenderSize( ImageController.DEFAULT_THUMBNAIL_SIZE );
       param.setSourceProgressivePasses( 0, 1 );

       final BufferedImage thumbnailImage = reader.read( 0, param );
       thumbnailImage.flush();


       //create an thumbnailImage info imageFile
       ImageInfo info = HxImage.createImageInfo( imageFile, new Dimension( width, height ) );

       return new HxImage( null, imageFile, thumbnailImage, null, info );
    }*/
    public static synchronized BufferedImage readThumbnail(final File imageFile) throws Exception {
        return readThumbnail(imageFile, LITTLE_THUMB)[0];
    }

    /**
     * This is the one being used.
     * reset  - dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 55950 ms. To read 91 images
     * <p/>
     * no reset dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 32938 ms. To read 91 images
     * <p/>
     * swingUtilities.invokeAndWait(new Runnable()
     * dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 44594 ms. To read 91 images
     * but the size is not alway correct!!
     * <p/>
     * swingUtilities.invokeLater(new Runnable()
     * {
     * public void run()
     * {
     * reader.dispose();
     * }
     * } );
     * <p/>
     * dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 39687 ms. To read 125 images
     * dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 41340 ms. To read 125 images
     * <p/>
     * nothing
     * dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 40458 ms. To read 125 images
     * dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 41520 ms. To read 125 images
     * <p/>
     * reader.dispose();
     * dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 39697 ms. To read 125 images
     * <p/>
     * no DB insertImage
     * dk.hewison.image.io.ImmediateImageReaderThreadreadImage.took 25167 ms. To read 125 images
     */
    public static synchronized BufferedImage[] readThumbnail(final File imageFile, int... thumbnailSizes) throws Exception {
        ImageInputStream iis = null;
        ImageReader reader = null;
        BufferedImage[] bufferedImages = new BufferedImage[thumbnailSizes.length];
        try {
            //if( HxLog.DEBUG_ON )
            //   HxLog.debug( getClass().getName() + ".readThumbnail " );

            Log.debug(".readThumbnail " + imageFile.getAbsolutePath());

            final String ext = getExtension(imageFile.getName());
            reader = getImageReader(ext);
            //reader.reset();

            iis = ImageIO.createImageInputStream(imageFile);
            reader.setInput(iis, true, true);

            final ImageReadParam param = reader.getDefaultReadParam();

            final int height = reader.getHeight(0);
            final int width = reader.getWidth(0);

            for (int i = 0; i < thumbnailSizes.length; i++) {
                int thumbnailSize = thumbnailSizes[i];


                int scale = calculateScale(width, height, thumbnailSize, thumbnailSize);

                if (scale < 1)
                    scale = 1;
                param.setSourceProgressivePasses(0, 4);
                param.setSourceSubsampling(scale, scale, 0, 0);
                //if( param.canSetSourceRenderSize() )
                //   param.getSourceRenderSize( ImageController.DEFAULT_THUMBNAIL_SIZE );
                //param.setSourceProgressivePasses( 0, 1 );

                bufferedImages[i] = reader.read(0, param);
                bufferedImages[i].flush();
            }

            return bufferedImages;

        } finally {
            if (iis != null)
                iis.close();

            if (reader != null)
                reader.dispose();

        }
    }

    private static String getExtension(String fileName) {
        return fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();

    }


    public BufferedImage readImage(final File imageFile) throws Exception {
        //final long start = System.currentTimeMillis();
        BufferedImage image;
        //ImageInputStream iis = ImageIO.createImageInputStream( imageFile );
        //HxImageReader reader = ( HxImageReader ) ImageIO.getImageReadersByFormatName( "jpg" ).next();
        //reader.setInput( iis, false );

        //ImageReadParam param = reader.getDefaultReadParam();
        //image = reader.read( 0, param );
        //reader.dispose();
        //iis.flush();
        //iis.close();
        final String name = imageFile.getName().toLowerCase();
        if (name.endsWith("jpg") || name.endsWith("jpeg")) {
            image = readJPEG(imageFile);
        } else {
            Log.debug("HxImageIO.readImage using ImageIO to read " + imageFile.getName());
            try {
                image = ImageIO.read(imageFile);
            } catch (IOException e) {
                e.printStackTrace();
                throw e;
            }
            if (image == null)
                throw new Exception("Not an image");
        }
        //Log.debug( "took " + ( System.currentTimeMillis() - start ) + "ms to open imageFile " + imageFile.getName() +
        //               " size " + imageFile.length() / 1000 + "k" );
        image.flush();
        return image;
    }

    public static BufferedImage readJPEG(final File imageFile) {
        //ImageInfo info = readImageInfo( imageFilename );
        //if( info.getModifiedTS() != imageFile.lastModified() )
        //   return null;

        BufferedImage image = null;

        try {
            //took 2423ms to open 204 files
            //image = ImageIO.read( imageFile );

            //took 1663ms to open 204 files
            //took 2033ms to open 204 files with very small thumbnails

            //GraphicsConfiguration.createCompatibleImage(w, h)
            final JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new FileInputStream(imageFile));
            image = decoder.decodeAsBufferedImage();
            image.flush();
        } catch (IOException e) {
            Log.debug("no imageFile" + e);
        } catch (Exception e) {
            Log.debug("HxImageReader.readJPEG could not read with JPEGImageDecoder ");
            try {
                image = ImageIO.read(imageFile);
                if (image != null)
                    image.flush();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        return image;
    }

    private static int calculateScale(final int width, final int height, int thumbWidth, int thumbHeight) {
        int scale;
        if (width < thumbWidth || height < thumbHeight)
            scale = 1;
        else
            scale = Math.max(width / thumbWidth, height / thumbHeight);
        //Log.debug( "image size = " + width + "x" + height );
        //Log.debug( "scale " + scale + " becomes " + width / scale + "x" + height / scale );
        /**
         * if image is 1000x800 and WIDTH is 100
         * then the image should be 100 x 80.
         * x is 10 therefore WIDTH is 100 and HEIGHT is 800/10 = 80
         */

        //Log.debug( "calculateScale scale " + scale + ":  :" + width + "::" + height );

        return scale;
    }

    /*public ImageInfo readImageInfoFromThumbnail( File imageFile ) throws Exception
    {
       return HxImage.createImageInfo( imageFile, getImageSize( imageFile ) );
    } */

    protected Dimension getImageSize(File imageFile) throws Exception {
        final String ext = getExtension(imageFile.getName());
        final ImageReader reader = getImageReader(ext);
        reader.reset();
        final ImageInputStream iis = ImageIO.createImageInputStream(imageFile);
        reader.setInput(iis);

        return new Dimension(reader.getWidth(0), reader.getHeight(0));
    }

    private static ImageReader getImageReader(final String ext) throws Exception {
        final Iterator readers = ImageIO.getImageReadersByFormatName(ext);
        if (readers.hasNext()) {
            return (ImageReader) readers.next();
        }
        return null;


        /*
       ImageReader reader = ( ImageReader ) mReaders.get( ext.toLowerCase() );
       if( reader == null )
       {
          Exception e = new Exception( "no reader for extension " + ext );
          Log.debug( getClass().getName() + ".readNewThumbnail " + e.getMessage() );
          throw e;
       }
       return reader;*/
    }

    public static BufferedImage readSubsampling(InputStream in, String suffix, int maxSize) throws IOException {
        ImageReader reader = getReader(suffix);
        reader.setInput(ImageIO.createImageInputStream(in), true, true);
        ImageReadParam param = reader.getDefaultReadParam();
        int size = Math.max(reader.getWidth(0), reader.getHeight(0));
        int subsampling = size / maxSize + (size % maxSize != 0 ? 1 : 0);
        param.setSourceSubsampling(subsampling, subsampling, 0, 0);
        BufferedImage image = reader.read(0, param);
        Log.debug("Size = " + size + ", maxSize = " + maxSize + ", w = " +
                image.getWidth() + ", h = " + image.getHeight());
        reader.dispose();
        return image;
    }

    public static ImageReader getReader(String path) throws IOException {
        String suffix = getSuffix(path);
        Iterator readers = ImageIO.getImageReadersBySuffix(suffix);
        if (!readers.hasNext())
            throw new IOException("No reader for suffix " + suffix);
        ImageReader reader = (ImageReader) readers.next();
        if (readers.hasNext())
            Log.debug("(There are more readers for suffix " + suffix + " )");
        return reader;
    }

    public static String getSuffix(String path) throws IOException {
        int dotIndex = path.lastIndexOf('.');
        if (dotIndex == -1)
            throw new IOException("Suffix not found for " + path);
        return path.substring(1 + dotIndex);
    }

    //-----------------writer---------------------

    public static void writeNewImage(BufferedImage image, File file) throws IOException {

        Log.debug(".writeNewImage " + file.getAbsolutePath());
        String ext = getExtension(file.getName());
        if (ext.equals("jpg") || ext.equals("jpeg"))
            writeNewJpegImage(image, removeExtention(file.getCanonicalPath()), QUALITY, true);
        else
            ImageIO.write(image, ext, file);
    }

    public void writeNewImage(BufferedImage image, String filename) throws IOException {
        String ext = getExtension(filename).toLowerCase();
        if (ext.equals("jpg") || ext.equals("jpeg"))
            writeNewJpegImage(image, removeExtention(filename), QUALITY, true);
        else
            ImageIO.write(image, ext, new File(filename));
    }

    public void writeNewJpegImage(final BufferedImage image, final String filenameNoExtension) throws IOException {
        Log.debug(getClass().getName() + ".writeNewJpegImage " + filenameNoExtension + " image width=" +
                image.getWidth() + " image height=" + image.getHeight());
        final File file = new File(filenameNoExtension + ".jpg");
        if (!file.getParentFile().exists())
            file.getParentFile().mkdirs();

        try {
            OutputStream os = new FileOutputStream(file);
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);
            //JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam( image );
            //param.setWriteImageOnly( false );
            //param.setQuality( quality, false );

            //JPEGCodec.createJPEGEncoder(new FileOutputStream( file ));

            //final JPEGImageEncoder encoder = new JPEGImageEncoder( new FileOutputStream( file ), param );
            encoder.encode(image);//, param );
            os.flush();
            os.close();
        } catch (IOException e) {
            Log.debug(getClass().getName() + ".writeNewJpegImage" + e);
            //ImageIO.write( image, "jpg", file );
        }
        //ImageIO.write( thumbnailImage, "jpg", cacheFile );

        image.flush();
        //ImageInfo info = createImageInfo( file, image );
        /*if( createThumbnail && file.length() > 30000 )
        {
           cacheImage( file );
        } */
    }

    public static File writeNewJpegImage(final BufferedImage image, final String filenameNoExtension, float quality,
                                         boolean forceOverwrite) throws IOException {
        final File file = new File(filenameNoExtension + ".jpg");
        if (!forceOverwrite && file.exists())
            return file;

        if (!file.getParentFile().exists())
            file.getParentFile().mkdirs();

        try {
            JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(new FileOutputStream(file));
            JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
            param.setQuality(QUALITY, true);

            encoder.encode(image, param);
        } catch (IOException e) {
            Log.debug(".writeNewJpegImage" + e);
            e.printStackTrace();
            //ImageIO.write( image, "jpg", file );
        }
        //ImageIO.write( thumbnailImage, "jpg", cacheFile );

        image.flush();
        //ImageInfo info = createImageInfo( file, image );
        /*if( createThumbnail && file.length() > 30000 )
        {
           cacheImage( file );
        } */
        return file;
    }


    public static String removeExtention(final String fileName) {
        //HxLog.debug( "Util.removeExtention " + fileName );
        int lastIndex = fileName.lastIndexOf('.');
        if (lastIndex != -1)
            return fileName.substring(0, lastIndex);
        return fileName;
    }


    public static void createThumbnail(File in) throws Exception {
        BufferedImage[] image = HxImageIO.readThumbnail(in, LITTLE_THUMB, BIG_THUMB);

        File littleThumb = new File(in.getParent() + File.separator + "." + in.getName().toLowerCase());
        File bigThumb = new File(in.getParent() + File.separator + ".a" + in.getName().toLowerCase());

        writeNewImage(image[0], littleThumb);
        writeNewImage(image[1], bigThumb);
    }

    public static void main(String[] args) {

        File input = new File("/Users/johnhewison/2009/04:01:2009/P1000592.JPG");
        File out = new File("/Users/johnhewison/2009/04:01:2009/xxxxP1000592.JPG");

        long start = System.currentTimeMillis();
        try {
            BufferedImage im = HxImageIO.readThumbnail(input);
            HxImageIO.writeNewImage(im, out);
            //HxImageIO.readThumbnail(new File("aa"), new File("/Users/johnhewison/2009/04:01:2009/P1000592.JPG"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        //HxImageIO.readJPEG(new File("/Users/johnhewison/2009/04:01:2009/P1000592.JPG"));

        /* try {
            Thumbnail.createThumb(input, 100,100,7,out);
        } catch (Exception e) {
            e.printStackTrace();
        }*/

        Log.debug("took " + (System.currentTimeMillis() - start));
    }
}
